"""HTTP 请求工具集

- 安全提取可序列化的 JSON 请求体
- 自动脱敏敏感字段
- 兼容文件上传 / form-data（直接返回 None）
- 分页参数处理工具
"""
import json
import logging
from typing import Any, Dict, Optional

from fastapi import Request, Query
from pydantic import BaseModel

logger = logging.getLogger(__name__)

SENSITIVE_KEYS = {"password", "oldPassword", "pwd", "token"}


def _mask_sensitive(data: Any) -> Any:
	"""递归脱敏"""
	if isinstance(data, dict):
		return {k: "***" if k in SENSITIVE_KEYS else _mask_sensitive(v) for k, v in data.items()}
	if isinstance(data, list):
		return [_mask_sensitive(item) for item in data]
	return data


async def safe_extract_body(request: Request) -> Optional[Dict[str, Any]]:
	"""
	从 FastAPI Request 中安全提取请求体

	1. 仅处理 Content-Type: application/json
	2. 使用 Starlette 缓存，不重复消耗字节流
	3. 返回已脱敏且可 JSON 序列化的 dict
	4. 异常 / 格式错误返回 None，不影响主流程

	:param request: FastAPI Request 对象
	:return: 脱敏后的 dict；非法或无法解析时返回 None
	"""
	content_type = request.headers.get("content-type", "")
	if "application/json" not in content_type:
		return None
	try:
		body = await request.json()
		masked = _mask_sensitive(body)
		# 二次 dumps 确保可序列化（如 datetime → str）
		return json.loads(json.dumps(masked, default=str))
	except Exception as e:
		logger.debug("skip body audit: %s", e)
		return None


# ==================== 分页工具 ====================

class PaginationParams(BaseModel):
	"""通用分页参数模型"""
	page: int = Query(1, ge=1, description="页码"),
	size: int = Query(20, ge=1, le=100, description="每页大小")
